package org.fast.develop.logicflow.core.meta.builder;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.fast.develop.logicflow.core.meta.base.Ref;
import org.fast.develop.logicflow.core.meta.base.line.Incoming;
import org.fast.develop.logicflow.core.meta.base.line.Line;
import org.fast.develop.logicflow.core.meta.base.properties.GetSetProperties;
import org.fast.develop.logicflow.core.meta.condition.ConditionExpression;
import org.fast.develop.logicflow.core.meta.exception.LogicFlowMetaException;
import org.fast.develop.logicflow.core.meta.base.Resource;
import org.fast.develop.logicflow.core.meta.base.line.Outgoing;
import org.fast.develop.logicflow.core.meta.shape.End;
import org.fast.develop.logicflow.core.meta.shape.Start;
import org.fast.develop.logicflow.core.meta.shape.control.IfElseControl;
import org.fast.develop.logicflow.core.meta.shape.line.SequenceFlow;
import org.fast.develop.logicflow.core.meta.shape.task.JavaClassTask;
import org.fast.develop.logicflow.core.meta.shape.task.ServiceTask;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author 莫寅
 * @createTime 2022-11-24 01:23
 * @desc：
 */
@Slf4j
public class ElementFactory {

    Map<String, Class<? extends Resource>> elementClasses = new HashMap<>();

    public ElementFactory() {
        elementClasses.put("start", Start.class);
        elementClasses.put("end", End.class);
        elementClasses.put("serviceTask", ServiceTask.class);
        elementClasses.put("javaClassTask", JavaClassTask.class);
        elementClasses.put("sequenceFlow", SequenceFlow.class);
        elementClasses.put("ifelse", IfElseControl.class);
    }

    public void registerElement(String name, Class<? extends Resource> resourceClass){
        if(elementClasses.get(name) != null){
            throw new LogicFlowMetaException(StrUtil.format("{}:注册名已存在，不要重复注册", name));
        }
        elementClasses.put(name, resourceClass);
    }

    public <T extends Resource>T create(Class<T> elementClass){
        try {
            return elementClass.newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

    public Resource create(JSONObject jsonObject){
        String type = jsonObject.getStr("type");

        Resource resource = getElement(type);
        setResourceId(resource, jsonObject);
        setIncoming(resource, jsonObject);
        setOutgoing(resource, jsonObject);
        setLine(resource, jsonObject);
        setProperties(resource, jsonObject);
        return resource;
    }

    public void setResourceId(Resource resource, JSONObject jsonObject){
        String resourceId = jsonObject.getStr("resourceId");
        resource.setResourceId(resourceId);
    }

    public void setProperties(Resource resource, JSONObject jsonObject){
        JSONObject propertiesJSONObject = jsonObject.getJSONObject("properties");

        if(propertiesJSONObject != null && resource instanceof GetSetProperties){
            GetSetProperties getSetProperties = (GetSetProperties)resource;

            getSetProperties.setProperties(
                    BeanUtil.toBean(propertiesJSONObject, getSetProperties.getPropertiesClass())
            );
        }


    }

    public void setLine(Resource resource, JSONObject jsonObject){
        if(resource instanceof Line){
            Line line = (Line)resource;

            String sourceRefResourceId = jsonObject.getStr("sourceRef");
            Ref sourceRef = new Ref();
            sourceRef.setResourceId(sourceRefResourceId);
            line.setSourceRef(sourceRef);


            String targetRefResourceId = jsonObject.getStr("targetRef");
            Ref targetRef = new Ref();
            targetRef.setResourceId(targetRefResourceId);
            line.setTargetRef(targetRef);

            String expression = jsonObject.getStr("conditionExpression");
            ConditionExpression conditionExpression = new ConditionExpression();
            conditionExpression.setExpression(expression);
            line.setCondition(conditionExpression);
        }
    }



    public void setOutgoing(Resource resource, JSONObject jsonObject){
        if(resource instanceof Outgoing){
            Outgoing outgoing = (Outgoing)resource;
            JSONArray outgoingJSONArray = jsonObject.getJSONArray("outgoing");
            List<Ref> resourceList = getResources(outgoingJSONArray);
            outgoing.setOutgoing(resourceList);
        }
    }

    private List<Ref> getResources(JSONArray resourceJSONArray){
        return resourceJSONArray.stream().map(s -> {
            JSONObject resourceJSONObject = (JSONObject)s;
            String resourceId = resourceJSONObject.getStr("resourceId");
            Ref resource = new Ref();
            resource.setResourceId(resourceId);
            return resource;
        }).collect(Collectors.toList());
    }

    public void setIncoming(Resource resource, JSONObject jsonObject){
        if(resource instanceof Incoming){
            Incoming incoming = (Incoming)resource;
            JSONArray incomingJSONArray = jsonObject.getJSONArray("incoming");
            List<Ref> resourceList = getResources(incomingJSONArray);

            incoming.setIncoming(resourceList);
        }
    }

    public Resource getElement(String type) {
        Class<? extends Resource> elementClass = elementClasses.get(type);
        try {
            return elementClass.newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NullPointerException e){
            log.error("type:{} 未找到", type);
            e.printStackTrace();
        }
        return null;
    }

    public String getType(Class<? extends Resource> elementClass){
        Map<Class<? extends Resource>, String> map = elementClasses.entrySet().stream().collect(Collectors.toMap(entity-> entity.getValue(),entity-> entity.getKey()));
        if(map.containsKey(elementClass)){
            return map.get(elementClass);
        } else {
            throw new LogicFlowMetaException(StrUtil.format("{}:该类未注册,获取失败", elementClass.getName()));
        }
    }


}
